/*
*	@file	hcomm.h
*	@author	Artur Gilmutdinov <arturg000@gmail.com>
*	@brief	Contains distributed system's client hcomm_t. 
*/ 

#pragma once
#include <iostream>
#include <vector>
#include <map>
#include "hsock.h"
#include "hcomms.h"

#define log log_t::log

using namespace std;

inline void red(char *t, int l)
{
	for (int i = 0; i<l; i++) t[i] = ' ';	
}

/*
*	@class	stubs_t
*	@brief	Abstract class for skeletons.
*/
class skel_t
{
	public:
	// Calls real object's methods.
	virtual char *call(int socket, char*) = 0;
};

/*
*	@class	stubc_t
*	@brief	Abstract class for client stubs.
*/
class stub_t
{
	int socket;
	public:
	
	// Called by user. Save file to local filesystem from other node.
	int downloadfile(char* name, char* path)
	{
		char bf[255];
		
		strcpy(bf, "upload\n");
		send(socket, bf, 255, 0);	
		strcpy(bf, name);
		strcat(bf,"\n");
		send(socket, bf, 255, 0);
		
		FILE * f = fopen(path, "w");
		int size;
		// Get node ip from node that is filesystem nameserver (not hcomms_t).
		recv(socket, bf, 255, 0);
		sscanf(bf, "%d", &size);
	
		while(size>255*sizeof(char))
		{
			recv(socket, bf, 255, 0);
			fwrite(bf,sizeof(char)*255, 1 , f);  
			size-=255*sizeof(char);	
		}
		recv(socket, bf, 255, 0);
		fwrite(bf,sizeof(char)*255, 1 , f);  
	fclose(f);	
	return 0;
	}
};

/*
*	@class	pathsolver_t
*	@brief	Abstract class for name resolving on filesystem's nameserver when client calls.
*/
class pathsolver_t
{
	public:
	virtual char *pathsolve(char*);
};

/*
*	@class	hcomm_t
*	@brief	Distributed system client. Share objects and inoce remote methods.
*/
class hcomm_t
{
	public:
	char srvip[255];
	int socket;
	int cliport, srvport;
	char name[255];
	pthread_t srv_th;
	// Local remote object's stubs.
	map<char*, skel_t*> obj;
	
	// Pathresolver.
	pathsolver_t *pathsolver;
	
	// Serve remote interface.
	void serve_ifs(int socket);
	//void serve_checkobj(int socket);
	
	public:
	
	hcomm_t(char *srvip, int port, const char *nm) 
	{ 
		srvport = port; 
		pathsolver = NULL; 
		strcpy(this->srvip, srvip);  
		strcpy(this->name, nm);
	}
	// Called by hsock when client connects.
	void serve(int socket, char *ip, int port);	
	
	// Called by serve when client whant to upload something.
	void uploadfile(int socket);
	void set_pathsolver(pathsolver_t *p) { pathsolver = p; }	
	
	// Share local object.
	template <class type, class stubt>
	void share_obj(type *data, char* name);
	// Open remote interface.
	template <class type>
	void open_ifs(type *cstub, char *name, int *error);
	
	// Start listening connections.
	void start_server();
	// join
	void join();
	// Connect to object naming server (hcomms_t).
	void connect();
};

// share obj
template <class type, class stubt>
void hcomm_t::share_obj(type *data, char* name)
	{
		// report to comm serv	
		hsock_t h;
	//	log("Connecting to nameserver");
		int socket = h.client(srvip, srvport);
	//	log("Connected");
		stubt *stub = new stubt(data);
		
		char *bf = new char [255];
		strcpy(bf, "addobj\n");
		send(socket, bf, 255, 0);
		sprintf(bf, "%s\n", this->name);
//		log(bf);
		send(socket, bf, 255, 0);
		sprintf(bf, "%s\n", name);
//		log(bf);
		send(socket, bf, 255, 0);
		pair<char*,skel_t*> o(bf, stub);
		 
		char bf1[255];
		strcpy(bf1,"");
		recv(socket, bf1, 255, 0);
//		log(bf1);
		
		obj.insert(o);
		close(socket);
	}
	
// return ifs
template <class type>
void hcomm_t::open_ifs(type *cstub, char *name, int *error)
	{
		// get ip obj
		// send request		
		hsock_t h;
//		log("Connecting to nameserver");
		int socket = h.client(srvip, srvport);
		
		char bf[255];
		char bf1[255];
		strcpy(bf, "ifs\n");
		send(socket, bf, 255, 0);
		sprintf(bf, "%s\n", name);
		send(socket, bf, 255, 0);
//		log("Recv ip");
		recv(socket, bf, 255, 0);
//		log(bf);
		strcpy(bf1, "");
		recv(socket, bf1, 255, 0);
		sscanf(bf1, "%d", &cliport);
//		log(bf1);
		close(socket);
		// connect to node
		hsock_t p;
//		log("Connecting to node");
		socket = p.client(bf, cliport);
//		log("Starting ifs");
		strcpy(bf, "ifs\n");
		send(socket, bf, 255, 0);
		sprintf(bf, "%s\n", name);
		send(socket, bf, 255, 0);
		
		cstub->socket = socket;
		if (error!=NULL)
		*error = 0;
	//	return cstub;
	}